package model;

import codigoObjeto.Assembly;
import codigoObjeto.Codigo;
import codigoObjeto.Operator;
import codigoObjeto.Registers;
import codigoObjeto.Rotulo;
import parserXMI.Codable;
import parserXMI.Typable;
import parserXMI.Type;
import parserXMI.Variable;

public class IfThenElse implements Typable, Codable{
	private Expression condition;
	private Codable then;
	private Codable elseCode;
	private Label next = Label.getLabel();
	private String code;
	
	public IfThenElse(Expression condition, Codable then, Codable elseCode){
		this.condition = condition;
		this.then = then;
		this.elseCode = elseCode;
	}

	public Label getNext() {
		return next;
	}

	public void setNext(Label next) {
		this.next = next;
	}

	public String getCode() {
		if(code == null || code.equals(""))
			gen3Address();
		return code;
	}

	public void setCode(String code) {
		this.code = code;
	}



	public Expression getCondition() {
		return condition;
	}


	public void setCondition(Expression condition) {
		this.condition = condition;
	}


	public Codable getThen() {
		return then;
	}


	public void setThen(Codable then) {
		this.then = then;
	}


	public Codable getElseCode() {
		return elseCode;
	}


	public void setElseCode(Codable elseCode) {
		this.elseCode = elseCode;
	}


	@Override
	public void gen3Address() {
		if(elseCode == null){
			getCondition().setETrue(Label.getLabel());
			Label ifNext = getNext();
			getThen().setNext(ifNext);
			getCondition().setEFalse(ifNext);
			setCode(getCondition().getCode() +" "+ getCondition().getETrue() + " "+ getThen().getCode() );
		}else{
			getCondition().setETrue(Label.getLabel());
			getCondition().setEFalse(Label.getLabel());
			Label ifNext = getNext();
			getElseCode().setNext(ifNext);
			getThen().setNext(ifNext);
			setCode(getCondition().getCode() + " " + Printer.label(getCondition().getETrue()) +
					" " + getThen().getCode() + Printer.gen("goto " + getNext()) + " " + Printer.label(getCondition().getEFalse()) +
					" " + getElseCode().getCode());
		}
		Printer.gen("goto " + getNext());
	}
	
	@Override
	public String genObjectCode() {
		
		Registers rl;
		
		//true ou false
		if (condition.getType1() instanceof Literal && condition.getType2() == null){
	
			Literal l = (Literal) condition.getType1();
			
			if (l.getValue().equals("true")){
				getThen().genObjectCode();
				addBranch("160");
			} else {
				getElseCode().genObjectCode();
			}
			
		} else if (condition.getType1() instanceof Literal && condition.getType2() instanceof Variable) {

			Literal l = (Literal) condition.getType1();
			Variable v = (Variable) condition.getType2();
			
			Assembly.add(new Codigo(Operator.SUB, l.getValue().toString(), v.getName()));
			
			if (condition.getOperation() != null){
				
				String escape = Registers.getMethodPosition()+"";
				if (condition.getOperation().getValue().equals(">")){
					Assembly.add(new Codigo(Operator.BLTZ, "R0", escape));
				
				} else if (condition.getOperation().getValue().equals("<")){
					Assembly.add(new Codigo(Operator.BGTZ, "R0", escape));
				
				} else if (condition.getOperation().getValue().equals("=")){
					Assembly.add(new Codigo(Operator.BNE, "R0", escape));
				} 
			}
			
			then.genObjectCode();
			elseCode.genObjectCode();
			
			
		} else if (condition.getType1() instanceof Expression && condition.getType2() instanceof Expression){
			
			Expression leftExpression = (Expression) condition.getType1();
			Expression rightExpression = (Expression) condition.getType2();
			
			rl = Registers.getRegister();
			Codigo newCode = new Codigo();
			newCode.setDestino(rl.getName());
			
			genCodeObjectRec((Codable) leftExpression);
			genCodeObjectRec((Codable) rightExpression);
			
			if (leftExpression.getOperation() != null){
				
				String escape = Registers.getMethodPosition()+"";
				if (leftExpression.getOperation().getValue().equals(">")){
					Assembly.add(new Codigo(Operator.BLTZ, "R0", escape));
					Assembly.add(new Codigo(Operator.BLTZ, "R1", escape));
				
				} else if (leftExpression.getOperation().getValue().equals("<")){
					Assembly.add(new Codigo(Operator.BGTZ, "R0", escape));
					Assembly.add(new Codigo(Operator.BGTZ, "R1", escape));
				
				} else if (leftExpression.getOperation().getValue().equals("=")){
					Assembly.add(new Codigo(Operator.BNE, "R0", escape));
					Assembly.add(new Codigo(Operator.BNE, "R1", escape));
				} 
			}

			//Condicao
			getCondition(leftExpression, rightExpression, newCode);
			
		}

		return null;
	}

	private void getCondition(Expression leftExpression, Expression rightExpression, Codigo newCode) {
		
		if (condition.getOperation().getValue().equals("=")){
			newCode.setOperator(Operator.BEQ);
		
			Registers subreg = Registers.getRegister();
			Codigo sub = new Codigo();
			sub.setOperator(Operator.SUB);
			sub.setDestino(subreg.getName());
			sub.setOperando("#" + rightExpression.getAddr() + ", #" + leftExpression.getAddr());
			
			Assembly.add(sub);
			Rotulo r = Rotulo.getNextRotulo();
			newCode.setRotulo(r);
			newCode.setDestino(sub.getDestino());
			Assembly.add(newCode);
			
			getThen().genObjectCode();
			Codigo ten = addBranchRotulo();
			getElseCode().genObjectCode();
			
			Rotulo fim = Rotulo.getNextRotulo();
			Rotulo.setRotulo(fim.getRotulo()-10);
			
			setBranchGoto(ten, fim.getRotulo()+"");
			
		} else {
			
			if (condition.getOperation().getValue().equals(">")){
				newCode.setOperando(rightExpression.getAddr() + ", " + leftExpression.getAddr());
				newCode.setOperator(Operator.SUB);
				Assembly.add(newCode);
				int caminho = newCode.getRotulo().getRotulo() + 50;
				Assembly.add(new Codigo(Operator.BGTZ, newCode.getDestino(), caminho+""));
				
			} else if (condition.getOperation().getValue().equals("<")){
				newCode.setOperando(rightExpression.getAddr() + ", " + leftExpression.getAddr());
				newCode.setOperator(Operator.SUB);
				Assembly.add(newCode);
				int caminho = newCode.getRotulo().getRotulo() + 40;
				Assembly.add(new Codigo(Operator.BLTZ, newCode.getDestino(), caminho+""));
				
			} else if (condition.getOperation().getValue().equals("+")){
				newCode.setOperando("#" + rightExpression.getAddr() + ", #" + leftExpression.getAddr());
				Assembly.add(newCode);
				newCode.setOperator(Operator.ADD);
			
			} else if (condition.getOperation().getValue().equals("-")){
				newCode.setOperando("#" + rightExpression.getAddr() + ", #" + leftExpression.getAddr());
				Assembly.add(newCode);
				newCode.setOperator(Operator.SUB);	
			}

			getThen().genObjectCode();
			addBranch();
			getElseCode().genObjectCode();
			addBranch();
			
		}
	}

	private void genCodeObjectRec(Codable tp){
		
		if (tp != null){
			
			if (tp instanceof Expression){
				Expression e = (Expression) tp;

				e.genObjectCode();
				
				if (e.getOperation() != null) {
					Assembly.setLastOperatorCode(Operator.SUB);
				}
				
				genCodeObjectRec((Codable) e.getType1());
				genCodeObjectRec((Codable) e.getType2());
				
			} 
		}
	}
	
	private void addBranch(){
		Codigo br = new Codigo();
		br.setOperator(Operator.BR);
		br.setDestino("*(O)SP");
		
		Assembly.add(br);
	}
	
	private void addBranch(String destino){
		Codigo br = new Codigo();
		br.setOperator(Operator.BR);
		br.setDestino(destino);
		
		Assembly.add(br);
	}
	
	private Codigo addBranchRotulo(){
		Codigo br = new Codigo();
		br.setOperator(Operator.BR);
		
		Assembly.add(br);
		return br;
	}
	
	private void setBranchGoto(Codigo c, String goTo){
		for (Codigo cod : Assembly.codigoObjeto) {
			if (cod.getRotulo().equals(c.getRotulo())) cod.setOperando(goTo);
		}
	}
	
	@Override
	public void setType(Type type) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public Type getType() {
		return ((Typable)getThen()).getType();
	}

	@Override
	public String getAddr() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setAddr(String temp) {
		// TODO Auto-generated method stub
		
	}
	
}